home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 26 / AACD 26.iso / AACD / Online / x3270 / unix_files / printer.c < prev    next >
Encoding:
C/C++ Source or Header  |  2008-11-14  |  10.5 KB  |  467 lines

  1. /*
  2.  * Copyright 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *    printer.c
  12.  *        Printer session support
  13.  */
  14.  
  15. #include "globals.h"
  16.  
  17. #if (defined(C3270) || defined(X3270_DISPLAY)) && defined(X3270_PRINTER) /*[*/
  18. #if defined(X3270_DISPLAY) /*[*/
  19. #include <X11/StringDefs.h>
  20. #include <X11/Xaw/Dialog.h>
  21. #endif /*]*/
  22. #include <errno.h>
  23. #include <signal.h>
  24. #include <time.h>
  25. #include <stdarg.h>
  26. #include <fcntl.h>
  27. #include "3270ds.h"
  28. #include "appres.h"
  29. #include "objects.h"
  30. #include "resources.h"
  31. #include "ctlr.h"
  32.  
  33. #include "ctlrc.h"
  34. #include "hostc.h"
  35. #include "menubarc.h"
  36. #include "popupsc.h"
  37. #include "printerc.h"
  38. #include "printc.h"
  39. #include "savec.h"
  40. #if defined(C3270) /*[*/
  41. #include "screenc.h"
  42. #endif /*]*/
  43. #include "tablesc.h"
  44. #include "telnetc.h"
  45. #include "trace_dsc.h"
  46. #include "utilc.h"
  47.  
  48. #define PRINTER_BUF    1024
  49.  
  50. /* Statics */
  51. static int      printer_pid = -1;
  52. #if defined(X3270_DISPLAY) /*[*/
  53. static Widget    lu_shell = (Widget)NULL;
  54. #endif /*]*/
  55. static struct pr3o {
  56.     int fd;            /* file descriptor */
  57.     unsigned long input_id;    /* input ID */
  58.     unsigned long timeout_id; /* timeout ID */
  59.     int count;        /* input count */
  60.     char buf[PRINTER_BUF];    /* input buffer */
  61. } printer_stdout = { -1, 0L, 0L, 0 },
  62.   printer_stderr = { -1, 0L, 0L, 0 };
  63.  
  64. static void    printer_output(void);
  65. static void    printer_error(void);
  66. static void    printer_otimeout(void);
  67. static void    printer_etimeout(void);
  68. static void    printer_dump(struct pr3o *p, Boolean is_err, Boolean is_dead);
  69. static void    printer_host_connect(Boolean connected unused);
  70.  
  71. /* Globals */
  72.  
  73. /*
  74.  * Printer Start-up function
  75.  * If 'lu' is non-NULL, then use the specific-LU form.
  76.  * If not, use the assoc form.
  77.  */
  78. void
  79. printer_start(const char *lu)
  80. {
  81.     const char *cmdlineName;
  82.     const char *cmdline;
  83.     const char *cmd;
  84.     int cmd_len = 0;
  85.     const char *s;
  86.     char *cmd_text;
  87.     char c;
  88.     int stdout_pipe[2];
  89.     int stderr_pipe[2];
  90.     static Boolean registered = False;
  91.  
  92. #if defined(X3270_DISPLAY) /*[*/
  93.     /* Make sure the popups are initted. */
  94.     printer_popup_init();
  95. #endif /*]*/
  96.  
  97.     /* Register interest in host connects and mode changes. */
  98.     if (!registered) {
  99.         register_schange(ST_CONNECT, printer_host_connect);
  100.         register_schange(ST_3270_MODE, printer_host_connect);
  101.         registered = True;
  102.     }
  103.  
  104.     /* Can't start two. */
  105.     if (printer_pid != -1) {
  106.         popup_an_error("printer is already running");
  107.         return;
  108.     }
  109.  
  110.     /* Gotta be in 3270 mode. */
  111.     if (!IN_3270) {
  112.         popup_an_error("Not in 3270 mode");
  113.         return;
  114.     }
  115.  
  116.     /* Select the command line to use. */
  117.     if (lu == CN) {
  118.         /* Associate with the current session. */
  119.  
  120.         /* Gotta be in TN3270E mode. */
  121.         if (!IN_TN3270E) {
  122.             popup_an_error("Not in TN3270E mode");
  123.             return;
  124.         }
  125.  
  126.         /* Gotta be connected to an LU. */
  127.         if (connected_lu == CN) {
  128.             popup_an_error("Not connected to a specific LU");
  129.             return;
  130.         }
  131.         lu = connected_lu;
  132.         cmdlineName = ResAssocCommand;
  133.     } else {
  134.         /* Specific LU passed in. */
  135.         cmdlineName = ResLuCommandLine;
  136.     }
  137.  
  138.     /* Fetch the command line and command resources. */
  139.     cmdline = get_resource(cmdlineName);
  140.     if (cmdline == CN) {
  141.         popup_an_error("%s resource not defined", cmdlineName);
  142.         return;
  143.     }
  144.     cmd = get_resource(ResPrinterCommand);
  145.     if (cmd == CN) {
  146.         popup_an_error("printer.command resource not defined");
  147.         return;
  148.     }
  149.  
  150.     /* Construct the command line. */
  151.  
  152.     /* Figure out how long it will be. */
  153.     cmd_len = strlen(cmdline) + 1;
  154.     s = cmdline;
  155.     while ((s = strstr(s, "%L%")) != CN) {
  156.         cmd_len += strlen(lu) - 3;
  157.         s += 3;
  158.     }
  159.     s = cmdline;
  160.     while ((s = strstr(s, "%H%")) != CN) {
  161.         cmd_len += strlen(hostname) - 3;
  162.         s += 3;
  163.     }
  164.     s = cmdline;
  165.     while ((s = strstr(s, "%C%")) != CN) {
  166.         cmd_len += strlen(cmd) - 3;
  167.         s += 3;
  168.     }
  169.  
  170.     /* Allocate a string buffer and substitute into it. */
  171.     cmd_text = Malloc(cmd_len);
  172.     cmd_text[0] = '\0';
  173.     for (s = cmdline; (c = *s) != '\0'; s++) {
  174.         char buf1[2];
  175.  
  176.         if (c == '%') {
  177.             if (!strncmp(s+1, "L%", 2)) {
  178.                 (void) strcat(cmd_text, lu);
  179.                 s += 2;
  180.                 continue;
  181.             } else if (!strncmp(s+1, "H%", 2)) {
  182.                 (void) strcat(cmd_text, hostname);
  183.                 s += 2;
  184.                 continue;
  185.             } else if (!strncmp(s+1, "C%", 2)) {
  186.                 (void) strcat(cmd_text, cmd);
  187.                 s += 2;
  188.                 continue;
  189.             }
  190.         }
  191.         buf1[0] = c;
  192.         buf1[1] = '\0';
  193.         (void) strcat(cmd_text, buf1);
  194.     }
  195.     trace_event("Printer command line: %s\n", cmd_text);
  196.  
  197.     /* Make pipes for printer's stdout and stderr. */
  198.     if (pipe(stdout_pipe) < 0) {
  199.         popup_an_errno(errno, "pipe() failed");
  200.         Free(cmd_text);
  201.         return;
  202.     }
  203.     (void) fcntl(stdout_pipe[0], F_SETFD, 1);
  204.     if (pipe(stderr_pipe) < 0) {
  205.         popup_an_errno(errno, "pipe() failed");
  206.         (void) close(stdout_pipe[0]);
  207.         (void) close(stdout_pipe[1]);
  208.         Free(cmd_text);
  209.         return;
  210.     }
  211.     (void) fcntl(stderr_pipe[0], F_SETFD, 1);
  212.  
  213.     /* Fork and exec the printer session. */
  214.     switch (printer_pid = fork()) {
  215.         case 0:    /* child process */
  216.         (void) dup2(stdout_pipe[1], 1);
  217.         (void) close(stdout_pipe[1]);
  218.         (void) dup2(stderr_pipe[1], 2);
  219.         (void) close(stderr_pipe[1]);
  220.         if (setsid() < 0) {
  221.             perror("setsid");
  222.             _exit(1);
  223.         }
  224.         (void) execlp("/bin/sh", "sh", "-c", cmd_text, CN);
  225.         (void) perror("exec(printer)");
  226.         _exit(1);
  227.         default:    /* parent process */
  228.         (void) close(stdout_pipe[1]);
  229.         printer_stdout.fd = stdout_pipe[0];
  230.         (void) close(stderr_pipe[1]);
  231.         printer_stderr.fd = stderr_pipe[0];
  232.         printer_stdout.input_id = AddInput(printer_stdout.fd,
  233.             printer_output);
  234.         printer_stderr.input_id = AddInput(printer_stderr.fd,
  235.             printer_error);
  236.         ++children;
  237.         break;
  238.         case -1:    /* error */
  239.         popup_an_errno(errno, "fork()");
  240.         (void) close(stdout_pipe[0]);
  241.         (void) close(stdout_pipe[1]);
  242.         (void) close(stderr_pipe[0]);
  243.         (void) close(stderr_pipe[1]);
  244.         break;
  245.     }
  246.  
  247.     Free(cmd_text);
  248.  
  249.     /* Tell everyone else. */
  250.     st_changed(ST_PRINTER, True);
  251. }
  252.  
  253. /* There's data from the printer session. */
  254. static void
  255. printer_data(struct pr3o *p, Boolean is_err)
  256. {
  257.     int space;
  258.     int nr;
  259.     static char exitmsg[] = "Printer session exited";
  260.  
  261.     /* Read whatever there is. */
  262.     space = PRINTER_BUF - p->count - 1;
  263.     nr = read(p->fd, p->buf + p->count, space);
  264.  
  265.     /* Handle read errors and end-of-file. */
  266.     if (nr < 0) {
  267.         popup_an_errno(errno, "printer session pipe input");
  268.         printer_stop();
  269.         return;
  270.     }
  271.     if (nr == 0) {
  272.         if (printer_stderr.timeout_id != 0L) {
  273.             /*
  274.              * Append a termination error message to whatever the
  275.              * printer process said, and pop it up.
  276.              */
  277.             p = &printer_stderr;
  278.             space = PRINTER_BUF - p->count - 1;
  279.             if (p->count && *(p->buf + p->count - 1) != '\n') {
  280.                 *(p->buf + p->count) = '\n';
  281.                 p->count++;
  282.                 space--;
  283.             }
  284.             (void) strncpy(p->buf + p->count, exitmsg, space);
  285.             p->count += strlen(exitmsg);
  286.             if (p->count >= PRINTER_BUF)
  287.                 p->count = PRINTER_BUF - 1;
  288.             printer_dump(p, True, True);
  289.         } else {
  290.             popup_an_error(exitmsg);
  291.         }
  292.         printer_stop();
  293.         return;
  294.     }
  295.  
  296.     /* Add it to the buffer, and add a NULL. */
  297.     p->count += nr;
  298.     p->buf[p->count] = '\0';
  299.  
  300.     /*
  301.      * If there's no more room in the buffer, dump it now.  Otherwise,
  302.      * give it a second to generate more output.
  303.      */
  304.     if (p->count >= PRINTER_BUF - 1) {
  305.         printer_dump(p, is_err, False);
  306.     } else if (p->timeout_id == 0L) {
  307.         p->timeout_id = AddTimeOut(1000,
  308.             is_err? printer_etimeout: printer_otimeout);
  309.     }
  310. }
  311.  
  312. /* The printer process has some output for us. */
  313. static void
  314. printer_output(void)
  315. {
  316.     printer_data(&printer_stdout, False);
  317. }
  318.  
  319. /* The printer process has some error output for us. */
  320. static void
  321. printer_error(void)
  322. {
  323.     printer_data(&printer_stderr, True);
  324. }
  325.  
  326. /* Timeout from printer output or error output. */
  327. static void
  328. printer_timeout(struct pr3o *p, Boolean is_err)
  329. {
  330.     /* Forget the timeout ID. */
  331.     p->timeout_id = 0L;
  332.  
  333.     /* Dump the output. */
  334.     printer_dump(p, is_err, False);
  335. }
  336.  
  337. /* Timeout from printer output. */
  338. static void
  339. printer_otimeout(void)
  340. {
  341.     printer_timeout(&printer_stdout, False);
  342. }
  343.  
  344. /* Timeout from printer error output. */
  345. static void
  346. printer_etimeout(void)
  347. {
  348.     printer_timeout(&printer_stderr, True);
  349. }
  350.  
  351. /* Dump pending printer process output. */
  352. static void
  353. printer_dump(struct pr3o *p, Boolean is_err, Boolean is_dead)
  354. {
  355.     if (p->count) {
  356.         /*
  357.          * Strip any trailing newline, and make sure the buffer is
  358.          * NULL terminated.
  359.          */
  360.         if (p->buf[p->count - 1] == '\n')
  361.             p->buf[--(p->count)] = '\0';
  362.         else if (p->buf[p->count])
  363.             p->buf[p->count] = '\0';
  364.  
  365.         /* Dump it and clear the buffer. */
  366. #if defined(X3270_DISPLAY) /*[*/
  367.         popup_printer_output(is_err, is_dead? NULL: printer_stop,
  368.             "%s", p->buf);
  369. #else /*][*/
  370.         action_output("%s", p->buf);
  371. #endif
  372.         p->count = 0;
  373.     }
  374. }
  375.  
  376. /* Close the printer session. */
  377. void
  378. printer_stop(void)
  379. {
  380.     /* Remove inputs. */
  381.     if (printer_stdout.input_id) {
  382.         RemoveInput(printer_stdout.input_id);
  383.         printer_stdout.input_id = 0L;
  384.     }
  385.     if (printer_stderr.input_id) {
  386.         RemoveInput(printer_stderr.input_id);
  387.         printer_stderr.input_id = 0L;
  388.     }
  389.  
  390.     /* Cancel timeouts. */
  391.     if (printer_stdout.timeout_id) {
  392.         RemoveTimeOut(printer_stdout.timeout_id);
  393.         printer_stdout.timeout_id = 0L;
  394.     }
  395.     if (printer_stderr.timeout_id) {
  396.         RemoveTimeOut(printer_stderr.timeout_id);
  397.         printer_stderr.timeout_id = 0L;
  398.     }
  399.  
  400.     /* Clear buffers. */
  401.     printer_stdout.count = 0;
  402.     printer_stderr.count = 0;
  403.  
  404.     /* Kill the process. */
  405.     if (printer_pid != -1) {
  406.         (void) killpg(printer_pid, SIGTERM);
  407.         printer_pid = -1;
  408.     }
  409.  
  410.     /* Tell everyone else. */
  411.     st_changed(ST_PRINTER, False);
  412. }
  413.  
  414. #if defined(X3270_DISPLAY) /*[*/
  415. /* Callback for "OK" button on printer specific-LU popup */
  416. static void
  417. lu_callback(Widget w, XtPointer client_data, XtPointer call_data unused)
  418. {
  419.     char *lu;
  420.  
  421.     if (w) {
  422.         lu = XawDialogGetValueString((Widget)client_data);
  423.         if (lu == CN || *lu == '\0') {
  424.             popup_an_error("Must supply an LU");
  425.             return;
  426.         } else
  427.             XtPopdown(lu_shell);
  428.     } else
  429.         lu = (char *)client_data;
  430.     printer_start(lu);
  431. }
  432. #endif /*]*/
  433.  
  434. /* Host connect/disconnect/3270-mode event. */
  435. static void
  436. printer_host_connect(Boolean connected unused)
  437. {
  438.     /*
  439.      * If we're no longer in 3270 mode, then we can no longer have a
  440.      * printer session.  This may cause some fireworks if there is a
  441.      * print job pending when we do this, so some sort of awful timeout
  442.      * may be needed.
  443.      */
  444.     if (!IN_3270)
  445.         printer_stop();
  446. }
  447.  
  448. #if defined(X3270_DISPLAY) /*[*/
  449. /* Pop up the LU dialog box. */
  450. void
  451. printer_lu_dialog(void)
  452. {
  453.     if (lu_shell == NULL)
  454.         lu_shell = create_form_popup("printerLu",
  455.             lu_callback, (XtCallbackProc)NULL, FORM_NO_WHITE);
  456.     popup_popup(lu_shell, XtGrabExclusive);
  457. }
  458. #endif /*]*/
  459.  
  460. Boolean
  461. printer_running(void)
  462. {
  463.     return printer_pid != -1;
  464. }
  465.  
  466. #endif /*]*/
  467.